/**
 * \file    generator.h
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 23, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#ifndef GENERATOR_H
#define GENERATOR_H

#include <limits>

#include "item.h"
#include "state.h"
#include "rule.h"
#include "grammar.h"
#include "common.h"
#include "builder.h"

LPP_NAMESPACE_BEGIN

class Generator
{
public:
    GrammarRef const & grammar() const {
        return grammar_m;
    }
    Generator(String const & __f
            , GrammarRef const & grammar
            , bool const shoud_compress_tables = true
            , bool const should_reorder_states = true)
        : file_name_m(__f)
        , number_of_errors_m(0)
        , grammar_m(grammar) {
        build_nullable();
        build_first_sets();
        build_states();
        build_terminal_lookaheads();
        build_reduce_actions();

        if (false && shoud_compress_tables) {
            DEBUG_MESSAGE("COMPRESS TABLES...");
            for (StateRef const & state: states_m) {
                state->compress_tables();
            }
#ifndef NDEBUG
            DEBUG_MESSAGE("SORT STATES...");
            for (StateRef & state: states_m) {
                for (ActionRef const & action: state->actions()) {
                    Format F("(%1%/%2%,%3%)=>%4%");
                    F%state->index()%state->id();
                    F%action->lookahead()->name();
                    F%Description()(action);
                    DEBUG_MESSAGE("%s",F.str().c_str());
                }
            }
#endif
            DEBUG_MESSAGE("COMPRESS TABLES... DONE");
        }
        if (should_reorder_states) {
            /* Reorder and renumber the states so that states
            ** with fewer choices occur at the end.
            ** This is an optimization that helps make the
            ** generated parser tables smaller.
            */
            sort_states();
        }
#ifndef NDEBUG
        if (number_of_errors_m>0) {
            DEBUG_MESSAGE("========== ERROR ========== ERROR ========== ERROR ==========");
            DEBUG_MESSAGE("========== NUMBER OF ERRORS WHILE GENERATING STATES: %ld",number_of_errors_m);
            DEBUG_MESSAGE("========== ERROR ========== ERROR ========== ERROR ==========");
        }
#endif
    }
    Index number_of_errors() const {
        return number_of_errors_m;
    }
    StateRefList const & states() const {
        return states_m;
    }
    StateRefList & states() {
        return states_m;
    }
    SymbolRefSet const symbols() const {
        SymbolRefSet out;
        for (auto const & S: grammar_m->symbols()) {
            out.insert(S);
        }
        return out;
    }
    ItemRef & get_item(StateRef const & state, const RuleRef &rule, SymbolIndex const dot);
    ItemRef const & get_item(const RuleRef &rule, SymbolIndex const dot) const;
    friend class State;
    Index number_of_states() const {
        return states_m.size();
    }
    StateRef const & state_at_index(Index const index) const {
#ifndef NDEBUG
        StateRef const & state = states().at(index);
        assert(index==state->index());
        return state;
#else
        return states().at(index);
#endif
    }
    ActionCode other_action_code() const;
private:
    StateRefList states_m;
    String const & file_name_m;
    Index number_of_errors_m;       /**< The total number of reduce errors. */
    GrammarRef grammar_m;
    typedef std::map<RuleRef,ItemRef> Rule2Item;// interest of unordered?
    typedef std::map<SymbolIndex,Rule2Item> Dot2R2I;
    Dot2R2I all_items_m;

    void build_nullable();
    void build_first_sets();
    void build_states();
    void build_terminal_lookaheads();
    void build_reduce_actions(bool show_conflicts = true);
    RuleIndex number_of_rules() const {
        return grammar_m->number_of_rules();
    }

    int resolve_conflict(ActionRef const & ax, ActionRef const & ay);
    void sort_states();
    void build_action_table();
public:
    ActionCode error_action_code() const;
private:
#warning guard against recursive call to a:=a
    void compute_closure(StateRef & state);
};

LPP_NAMESPACE_END

#endif // GENERATOR_H

